Skip to content

Migrate to SDK-style net9.0-windows#1

Merged
SysAdminDoc merged 4 commits into
mainfrom
feat/sdk-migration
Jun 14, 2026
Merged

Migrate to SDK-style net9.0-windows#1
SysAdminDoc merged 4 commits into
mainfrom
feat/sdk-migration

Conversation

@SysAdminDoc

Copy link
Copy Markdown
Owner

Migrates RED++ from .NET Framework 4.8.1 (old-style csproj + packages.config) to an SDK-style project targeting net9.0-windows (WPF + WinForms retained until the WinForms UI is retired). Zero behavior change to the verified scan/delete engine — every P/Invoke struct and safety invariant is untouched.

Why

4.8.1 is terminal and forced build/resource friction: a preserialized-resource incompatibility, a missing targeting pack, and no clean build without Visual Studio. The net481 SDK workaround even left the GUI unlaunchable locally (a FileLoadException on iconProject). Modern .NET fixes all of that and unlocks a genuine self-contained single-file publish.

What changed

  • RED/RED+.csproj rewritten as Microsoft.NET.Sdk (net9.0-windows, UseWPF/UseWindowsForms, glob compile). GenerateAssemblyInfo=false keeps AssemblyInfo.cs as the single version source. Output stays at repo-root bin\<Config>\RED+.exe (no TFM subfolder) so CI/tooling paths are unchanged.
  • Dropped packages.config and app.config (DPI awareness stays in app.manifest PerMonitorV2; no binding redirects needed).
  • RED.Tests retargeted to net9.0-windows with a direct ProjectReference (was a HintPath to the MSBuild-built exe); InternalsVisibleTo preserved.
  • Engine runtime-adaptation fixes (faithful, behavior-identical):
    • Directory.GetAccessControl(path)DirectoryInfo.GetAccessControl() (removed static; same default ACL sections) in the deletion lock check.
    • Version reads use Environment.ProcessPath / the AssemblyFileVersion attribute instead of Assembly.Location — the latter is empty in a single-file bundle and crashed -version/-json (caught by smoking the published artifact).
  • CI (ci.yml): MSBuild → dotnet build/dotnet test; safety smoke retained.
  • Release (release.yml): dotnet publish self-contained single-file win-x64 (no runtime install needed), with a pre-release safety smoke gate on the published artifact. winget manifest arch x86x64.

Verification

  • dotnet build -c Release — clean, no targeting-pack hacks, no preserialized-resource workarounds.
  • dotnet test50/50 pass on the new TFM (incl. the USN/MFT parser bounds tests that lock the P/Invoke offsets).
  • ✅ Headless safety smoke: empty-dir/file deletion; reparse-point/junction, deny-ACL fail-closed, and AutoProtectRoot all protected; dry-run exit codes (0/11); recycle→undo round-trip.
  • GUI renders locally (the migration payoff): WPF shell (dark navy) and classic WinForms (-classic) in dark + light; Extras menu enumerated via UI Automation.
  • Self-contained single-file artifact validated end-to-end: headless delete + -version + GUI render from the bundle.

Safety-critical change to a file-deleting tool — reviewed as a unit; the engine and its tests are unchanged.

.NET Framework 4.8.1 is terminal and forced build/resource friction
(preserialized-resource incompatibility, a missing targeting pack, and no
clean build without Visual Studio). Move to an SDK-style project targeting
net9.0-windows; WPF and WinForms are both retained until the WinForms UI is
retired.

The scan/delete engine and every P/Invoke struct are unchanged. Two
runtime-adaptation fixes were required:
- Directory.GetAccessControl(path) was removed on modern .NET; use the
  DirectoryInfo.GetAccessControl() extension (same default ACL sections) in
  the deletion lock check.
- Read the file version from Environment.ProcessPath / the embedded
  AssemblyFileVersion attribute instead of Assembly.Location, which is empty
  in a published single-file bundle and crashed -version and -json.

Drop packages.config and app.config (DPI awareness stays in app.manifest;
no binding redirects are needed). RED.Tests retargets to net9.0-windows and
references the engine project directly; the 50-test suite and the headless
safety smoke pass unchanged.
The SDK-style project no longer needs Visual Studio / MSBuild. Replace the
setup-msbuild + msbuild steps with dotnet build and keep the dotnet test and
headless safety-smoke steps. The smoke now creates its junction with
New-Item -ItemType Junction (no cmd dependency); RED+.exe writes to the
piped stdout that the existing Tee-Object capture asserts on.
Publish with dotnet (-p:PublishSingleFile=true --self-contained) so the
release needs no .NET runtime installed on the target machine, preserving
RED++'s unzip-and-run portability. WPF and reflection-based resource loading
are not trim-safe, so trimming is left off. A safety smoke runs against the
published artifact before the release is created, so a build that fails the
reparse-point / deny-ACL / AutoProtectRoot invariants cannot ship. The winget
manifest architecture moves x86 -> x64 to match the published artifact, and
publish/ is gitignored.
System Requirements no longer list .NET Framework 4.8.1: the release is a
self-contained single-file build that bundles .NET 9, so there is nothing to
install. Record the migration under the Unreleased changelog section.
@SysAdminDoc SysAdminDoc merged commit cb7f9ca into main Jun 14, 2026
1 check passed
@SysAdminDoc SysAdminDoc deleted the feat/sdk-migration branch June 14, 2026 06:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant